freemd 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.es.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](./README.md) · **Español**
4
4
 
5
- Una app de notas en markdown *local-first* que vive en tu máquina. Un comando y estás editando notas en tu navegador — interfaz dark limpia, vista previa en vivo, imágenes por nota y exportación a PDF o Word en un click.
5
+ Una app de notas en markdown *local* que vive en tu máquina. Un comando y estás editando notas en tu navegador — interfaz dark limpia, vista previa en vivo, imágenes por nota que se pueden agregar desde el portapapeles y exportación a PDF o Word en un click.
6
6
 
7
7
  ```bash
8
8
  npx freemd
@@ -10,6 +10,8 @@ npx freemd
10
10
 
11
11
  Eso es todo: crea `~/freemd` si no existe, levanta un servidor local en [http://localhost:5813](http://localhost:5813) y abre tu navegador.
12
12
 
13
+ ![img1](./doc/img1.jpeg)
14
+
13
15
  ## Por qué freemd
14
16
 
15
17
  - **Tus notas son archivos simples.** Todo es markdown en tu propio disco: sin cuentas, sin nube, sin ataduras. Puedes respaldarlas, sincronizarlas o buscarlas con grep.
@@ -71,12 +73,12 @@ Todo vive en una carpeta (por defecto `~/freemd`):
71
73
 
72
74
  ## La app
73
75
 
74
- - **Sidebar** — tus notas y carpetas. Al pasar el cursor sobre un elemento aparecen renombrar (✏️) y eliminar (🗑); los iconos de la cabecera crean notas, carpetas y suben adjuntos a la nota actual.
75
- - **Editor con tres modos** — *Edición* (markdown crudo), *Split* (editor y vista renderizada lado a lado, con scroll sincronizado), *Vista* (solo preview). Los bloques de código llevan resaltado de sintaxis.
76
+ - **Sidebar** — tus notas y carpetas. Al pasar el cursor sobre un elemento aparecen renombrar (✏️) y eliminar (🗑); los iconos de la cabecera crean notas, carpetas y suben adjuntos a la nota actual. Puedes plegarla con el botón de panel o `Cmd/Ctrl + B` (el estado se recuerda).
77
+ - **Editor con tres modos** — *Edit* (markdown crudo), *Split* (editor y vista renderizada lado a lado, con scroll sincronizado), *Preview* (solo preview). Los bloques de código llevan resaltado de sintaxis.
76
78
  - **Panel de recursos** — panel plegable a la derecha con las imágenes de la nota actual en miniaturas. Haz click en una para insertar su markdown en el cursor; al pasar el cursor puedes eliminarla.
77
79
  - **Pegar imágenes** — copia una captura o imagen y pulsa `Cmd/Ctrl + V` sobre la nota: se guarda en la carpeta de la nota y se inserta su markdown en el cursor.
78
- - **Autoguardado** — los cambios se guardan solos 1.2 s después de dejar de escribir (o al instante con `Cmd/Ctrl + S`).
79
- - **Exportar** — un click para descargar la nota como **PDF** o **Word (.docx)**: tema claro, código resaltado, imágenes incrustadas y con el nombre de la nota (`recetas.pdf`, nunca `index.pdf`). Si hay cambios sin guardar, se guardan primero.
80
+ - **Autosave** — los cambios se guardan solos 1.2 s después de dejar de escribir (o al instante con `Cmd/Ctrl + S`).
81
+ - **Export** — un click para descargar la nota como **PDF** o **Word (.docx)**: tema claro, código resaltado, imágenes incrustadas y con el nombre de la nota (`recetas.pdf`, nunca `index.pdf`). Si hay cambios sin guardar, se guardan primero.
80
82
 
81
83
  ## Desarrollo
82
84
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **English** · [Español](./README.es.md)
4
4
 
5
- A local-first markdown notes app that lives on your machine. One command and you're editing notes in your browser — clean dark UI, live preview, per-note image attachments, and one-click export to PDF or Word.
5
+ A *local* Markdown notes app that lives on your machine. One command and you're editing notes in your browser — a clean dark interface, live preview, per-note images you can paste directly from your clipboard, and one-click export to PDF or Word.
6
6
 
7
7
  ```bash
8
8
  npx freemd
@@ -10,6 +10,8 @@ npx freemd
10
10
 
11
11
  That's it: it creates `~/freemd` if it doesn't exist, starts a local server at [http://localhost:5813](http://localhost:5813) and opens your browser.
12
12
 
13
+ ![img1](./doc/img1.jpeg)
14
+
13
15
  ## Why freemd
14
16
 
15
17
  - **Your notes are plain files.** Everything is markdown on your own disk, no accounts, no cloud, no lock-in. Back it up, sync it, grep it, own it.
@@ -71,8 +73,8 @@ Everything lives in one folder (by default `~/freemd`):
71
73
 
72
74
  ## The app
73
75
 
74
- - **Sidebar** — your notes and folders. Hover any item to rename (✏️) or delete (🗑); the toolbar icons create notes, folders and upload attachments to the current note.
75
- - **Editor with three view modes** — *Edición* (raw markdown), *Split* (editor and rendered preview side by side, scroll-synced), *Vista* (rendered preview). Code blocks get syntax highlighting.
76
+ - **Sidebar** — your notes and folders. Hover any item to rename (✏️) or delete (🗑); the toolbar icons create notes, folders and upload attachments to the current note. Collapse it with the panel button or `Cmd/Ctrl + B` (state is remembered).
77
+ - **Editor with three view modes** — *Edit* (raw markdown), *Split* (editor and rendered preview side by side, scroll-synced), *Preview* (rendered only). Code blocks get syntax highlighting.
76
78
  - **Resources panel** — a collapsible panel on the right showing the current note's images as thumbnails. Click one to insert its markdown at the cursor; hover to delete it.
77
79
  - **Paste images** — copy a screenshot or image and press `Cmd/Ctrl + V` anywhere in the note: it's saved into the note's folder and its markdown inserted at the cursor.
78
80
  - **Autosave** — changes save automatically 1.2 s after you stop typing (or instantly with `Cmd/Ctrl + S`).
package/bin/openmd.js CHANGED
@@ -17,18 +17,18 @@ const valueOf = (...flags) => {
17
17
 
18
18
  if (hasFlag('-h', '--help')) {
19
19
  console.log(`
20
- freemd — editor de notas markdown en el navegador
20
+ freemd — markdown notes editor in your browser
21
21
 
22
- Uso:
23
- npx freemd [opciones]
22
+ Usage:
23
+ npx freemd [options]
24
24
 
25
- Opciones:
26
- -p, --port <n> Puerto del servidor (por defecto 5813)
27
- -d, --dir <ruta> Carpeta de notas (por defecto ~/freemd)
28
- -H, --host <addr> Escuchar en otra interfaz; sin valor usa 0.0.0.0 (toda la red local)
29
- --no-open No abrir el navegador automáticamente
30
- -v, --version Mostrar versión
31
- -h, --help Mostrar esta ayuda
25
+ Options:
26
+ -p, --port <n> Server port (default 5813)
27
+ -d, --dir <path> Notes folder (default ~/freemd)
28
+ -H, --host <addr> Listen on another interface; no value binds 0.0.0.0 (whole local network)
29
+ --no-open Do not open the browser automatically
30
+ -v, --version Print version
31
+ -h, --help Show this help
32
32
  `);
33
33
  process.exit(0);
34
34
  }
@@ -45,7 +45,7 @@ if (portArg === undefined) {
45
45
  } else {
46
46
  port = Number(portArg);
47
47
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
48
- console.error(`Puerto inválido: ${portArg}`);
48
+ console.error(`Invalid port: ${portArg}`);
49
49
  process.exit(1);
50
50
  }
51
51
  }
@@ -63,18 +63,18 @@ try {
63
63
  const { url, root, migrated } = await start({ port, dir, host, open });
64
64
  console.log('');
65
65
  console.log(` freemd ${pkg.version}`);
66
- console.log(` notas ${root}`);
67
- if (migrated) console.log(` migrada ${migrated} → ${root}`);
68
- console.log(` listo ${url}`);
66
+ console.log(` notes ${root}`);
67
+ if (migrated) console.log(` migrated ${migrated} → ${root}`);
68
+ console.log(` ready ${url}`);
69
69
  if (host === '0.0.0.0' || host === '::') {
70
70
  for (const address of lanAddresses(port)) {
71
- console.log(` red ${address}`);
71
+ console.log(` network ${address}`);
72
72
  }
73
73
  }
74
74
  console.log('');
75
75
  } catch (error) {
76
76
  if (error.code === 'EADDRINUSE') {
77
- console.error(`El puerto ${port} ya está en uso. Prueba con --port <otro puerto>`);
77
+ console.error(`Port ${port} is already in use. Try --port <another port>`);
78
78
  } else {
79
79
  console.error(error.message ?? error);
80
80
  }
@@ -0,0 +1 @@
1
+ pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-formula{color:#c678dd}.hljs-section,.hljs-name,.hljs-selector-tag,.hljs-deletion,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string{color:#98c379}.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-number{color:#d19a66}.hljs-symbol,.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-title.class_,.hljs-class .hljs-title{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}:root{--bg: #282c34;--bg-darker: #21252b;--bg-darkest: #1b1e24;--bg-panel: #2c313a;--bg-hover: #333842;--border: #3a3f4b;--border-dark: #181a1f;--fg: #abb2bf;--fg-bright: #d7dae0;--fg-dim: #5c6370;--red: #e06c75;--orange: #d19a66;--yellow: #e5c07b;--green: #98c379;--cyan: #56b6c2;--blue: #61afef;--purple: #c678dd;--mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace}*{box-sizing:border-box}html,body,#app{height:100%;margin:0}body{background:var(--bg);color:var(--fg);font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:14px;-webkit-font-smoothing:antialiased}::selection{background:#61afef47}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:5px;border:2px solid transparent;background-clip:content-box}::-webkit-scrollbar-thumb:hover{background:var(--fg-dim);border:2px solid transparent;background-clip:content-box}button{font-family:inherit}.app{display:flex;height:100%;overflow:hidden}.main{flex:1;display:flex;flex-direction:column;min-width:0;position:relative}.sidebar-slot{width:280px;min-width:280px;flex-shrink:0;overflow:hidden;transition:width .18s ease,min-width .18s ease}.sidebar-slot.collapsed{width:0;min-width:0}.sidebar{width:280px;min-width:280px;height:100%;display:flex;flex-direction:column;background:var(--bg-darker);border-right:1px solid var(--border-dark)}.side-head{display:flex;align-items:center;justify-content:space-between;height:48px;padding:0 10px 0 16px;border-bottom:1px solid var(--border-dark);flex-shrink:0}.brand{font-size:15px;font-weight:600;letter-spacing:.4px;color:var(--fg-bright)}.brand-md{color:var(--green)}.head-actions{display:flex;gap:2px}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:none;border-radius:6px;background:none;color:var(--fg-dim);cursor:pointer}.icon-btn:hover:not(:disabled){color:var(--fg-bright);background:var(--bg-panel)}.icon-btn:disabled{opacity:.35;cursor:default}.new-item{padding:10px;border-bottom:1px solid var(--border-dark)}.new-item input{width:100%;padding:7px 10px;font-size:13px;color:var(--fg-bright);background:var(--bg-darkest);border:1px solid var(--blue);border-radius:6px;outline:none}.new-item input::placeholder{color:var(--fg-dim)}.tree{flex:1;overflow-y:auto;padding:8px 6px}.tree-loading,.tree-empty{padding:16px 12px;font-size:12.5px;line-height:1.6;color:var(--fg-dim)}.item{display:flex;align-items:center;border-radius:6px}.item .row{flex:1;display:flex;align-items:center;gap:7px;min-width:0;padding:5px 8px;border:none;border-radius:6px;background:none;color:var(--fg);font-size:13px;text-align:left;cursor:pointer}.item .row:hover{background:var(--bg-panel)}.item.active .row{background:var(--bg-panel);color:var(--fg-bright)}.item .label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chev{display:inline-flex;align-items:center;justify-content:center;width:11px;flex-shrink:0;color:var(--fg-dim)}.chev svg{transition:transform .15s ease}.chev.open svg{transform:rotate(90deg)}.icon{display:inline-flex;flex-shrink:0}.t-note{color:var(--purple)}.t-folder{color:var(--blue)}.t-md{color:var(--cyan)}.t-image{color:var(--orange)}.t-file{color:var(--fg-dim)}.item .del{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;margin-right:4px;padding:0;border:none;border-radius:6px;background:none;color:var(--fg-dim);cursor:pointer;visibility:hidden;flex-shrink:0}.rename-item{padding:2px 6px 2px 0}.rename-item input{width:100%;padding:4px 8px;font-size:13px;color:var(--fg-bright);background:var(--bg-darkest);border:1px solid var(--blue);border-radius:6px;outline:none}.item:hover .del{visibility:visible}.item .del:hover{color:var(--red);background:var(--bg-panel)}.side-foot{display:flex;flex-direction:column;gap:8px;padding:12px;border-top:1px solid var(--border-dark);flex-shrink:0}.upload-btn{display:flex;align-items:center;justify-content:center;gap:8px;padding:8px;font-size:12.5px;color:var(--fg);background:var(--bg-panel);border:1px solid var(--border);border-radius:8px;cursor:pointer}.upload-btn:hover:not(:disabled){color:var(--blue);border-color:var(--blue)}.upload-btn:disabled{opacity:.4;cursor:default}.root-hint{font-size:11px;font-family:var(--mono);color:var(--fg-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toolbar{display:flex;align-items:center;gap:12px;height:48px;padding:0 16px;background:var(--bg-darker);border-bottom:1px solid var(--border-dark);flex-shrink:0}.meta{display:flex;align-items:center;gap:9px;min-width:0}.meta .name{font-size:13px;font-weight:500;color:var(--fg-bright);white-space:nowrap}.meta .crumb{font-size:12px;color:var(--fg-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.unsaved{width:7px;height:7px;border-radius:50%;background:var(--yellow);flex-shrink:0}.spacer{flex:1}.count{font-size:12px;color:var(--fg-dim);white-space:nowrap}.seg{display:flex;padding:2px;background:var(--bg-darkest);border:1px solid var(--border);border-radius:8px}.seg button{padding:5px 12px;font-size:12px;border:none;border-radius:6px;background:transparent;color:var(--fg-dim);cursor:pointer}.seg button.active{background:var(--bg-panel);color:var(--fg-bright)}.save-btn{display:flex;align-items:center;gap:7px;padding:6px 12px;font-size:12.5px;color:var(--blue);background:var(--bg-panel);border:1px solid var(--border);border-radius:8px;cursor:pointer}.save-btn:hover:not(:disabled){border-color:var(--blue)}.save-btn:disabled{color:var(--fg-dim);opacity:.6;cursor:default}.export-wrap{position:relative}.export-chev{display:inline-flex;color:var(--fg-dim)}.export-chev svg{transition:transform .15s ease}.export-chev.open svg{transform:rotate(180deg)}.menu-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:44;padding:0;border:none;background:transparent;cursor:default}.export-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:45;display:flex;flex-direction:column;min-width:150px;padding:4px;background:var(--bg-darker);border:1px solid var(--border);border-radius:8px;box-shadow:0 10px 32px #00000073}.export-menu button{display:flex;align-items:center;gap:8px;padding:8px 10px;border:none;border-radius:6px;background:none;color:var(--fg);font-size:12.5px;text-align:left;cursor:pointer}.export-menu button:hover{background:var(--bg-panel);color:var(--fg-bright)}.editor-body{flex:1;display:flex;min-height:0}.autosaved{font-size:11.5px;color:var(--green);white-space:nowrap}.panel-btn.active{color:var(--blue);background:var(--bg-panel)}.resources{width:240px;flex-shrink:0;display:flex;flex-direction:column;background:var(--bg-darker);border-left:1px solid var(--border-dark);overflow:hidden}.res-head{display:flex;align-items:center;justify-content:space-between;height:40px;padding:0 6px 0 14px;font-size:11px;font-weight:600;letter-spacing:.8px;text-transform:uppercase;color:var(--fg-dim);border-bottom:1px solid var(--border-dark);flex-shrink:0}.res-actions{display:flex;gap:2px}.res-empty{padding:14px;font-size:12px;line-height:1.6;color:var(--fg-dim)}.res-scroll{flex:1;overflow-y:auto}.res-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:10px}.res-item{position:relative;aspect-ratio:1;padding:0;border:1px solid var(--border);border-radius:6px;background:var(--bg-panel);overflow:hidden;cursor:pointer}.res-item:hover{border-color:var(--blue)}.res-remove{position:absolute;top:4px;right:4px;display:flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;border:none;border-radius:4px;background:#181a1fd9;color:var(--red);cursor:pointer;opacity:0}.res-item:hover .res-remove,.res-remove:focus-visible{opacity:1}.res-remove:hover{background:var(--bg-darkest)}.res-item img{display:block;width:100%;height:100%;object-fit:cover}.res-file{display:flex;align-items:center;justify-content:center;height:100%;color:var(--fg-dim)}.res-name{position:absolute;left:4px;right:4px;bottom:4px;padding:2px 6px;font-size:10px;text-align:left;color:#fff;background:#181a1fcc;border-radius:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}textarea.code{flex:1;width:100%;padding:24px 32px;border:none;outline:none;resize:none;background:var(--bg);color:var(--fg);font-family:var(--mono);font-size:14px;line-height:1.75;caret-color:var(--blue)}textarea.code.half{flex:1 1 50%;width:auto;min-width:0;padding:20px 24px;border-right:1px solid var(--border-dark)}.preview-wrap.half{flex:1 1 50%;min-width:0}.preview-wrap.half .preview{padding:24px 24px 60px}.preview-wrap{flex:1;overflow-y:auto}.preview{max-width:760px;margin:0 auto;padding:36px 48px 80px;font-size:15px;line-height:1.75}.preview h1,.preview h2,.preview h3,.preview h4{color:var(--fg-bright);line-height:1.3;margin:1.6em 0 .6em}.preview h1,.preview h2{padding-bottom:.3em;border-bottom:1px solid var(--border)}.preview h1{margin-top:.4em}.preview p{margin:.8em 0}.preview a{color:var(--blue);text-decoration:none}.preview a:hover{text-decoration:underline}.preview code{padding:2px 6px;border-radius:4px;background:#3a3f4b;color:var(--yellow);font-family:var(--mono);font-size:.88em}.preview pre{padding:16px;overflow-x:auto;background:var(--bg-darker);border:1px solid var(--border);border-radius:8px;line-height:1.6}.preview pre code{padding:0;background:none;color:inherit;font-size:13px}.preview blockquote{margin:1em 0;padding:2px 16px;color:var(--fg-dim);border-left:3px solid var(--fg-dim)}.preview ul,.preview ol{padding-left:1.6em}.preview li{margin:.3em 0}.preview img{max-width:100%;border-radius:6px}.preview hr{margin:1.6em 0;border:none;border-top:1px solid var(--border)}.preview table{border-collapse:collapse;margin:1em 0;width:100%}.preview th,.preview td{padding:7px 12px;border:1px solid var(--border)}.preview th{background:var(--bg-panel);color:var(--fg-bright)}.media-view{flex:1;display:flex;align-items:center;justify-content:center;overflow:auto;padding:32px}.media-view img{max-width:100%;max-height:100%;border-radius:8px;box-shadow:0 8px 40px #0006}.empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--fg-dim);position:relative}.sidebar-open{position:absolute;top:9px;left:10px}.primary-btn{display:flex;align-items:center;gap:8px;margin-top:8px;padding:9px 18px;font-size:13px;font-weight:600;color:var(--bg-darkest);background:var(--green);border:none;border-radius:8px;cursor:pointer}.primary-btn:hover{filter:brightness(1.1)}.logo{margin:0;font-size:30px;font-weight:600;letter-spacing:1px;color:#3e4451}.toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%);padding:10px 16px;font-size:13px;color:var(--fg-bright);background:var(--bg-darkest);border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 30px #00000059;z-index:100;animation:rise .2s ease}.toast.error{color:var(--red);border-color:var(--red)}@keyframes rise{0%{opacity:0;transform:translate(-50%) translateY(8px)}to{opacity:1;transform:translate(-50%) translateY(0)}}